-- #############################################################################
-- mpi-tests.lua
-- 
-- by Jacob Smith <http://code.google.com/p/lua-mpi/>
-- Copyright (C) 2009 Jacob Smith, All Rights Reserved
-- 
-- MPI test suite.
-- 
-- $Id: mpi-tests.lua 11 2009-07-01 08:02:38Z kloptops $

-- #############################################################################
-- Import stuff we require.
require('mpi');



-- Test vectors
--
-- If you intend to change any of these values, you must also recompute
-- the corresponding solutions below.  Basically, these are just hex
-- strings (for the big integers) or integer values (for the digits).
-- 
-- The comparison tests think they know what relationships hold between
-- these values.  If you change that, you may have to adjust the code
-- for the comparison tests accordingly.  Most of the other tests
-- should be fine as long as you re-compute the solutions, though.

local mp1 = "639A868CDA0C569861B";
local mp1raw = {  -- same as mp1, just as a string of octets
  0x00, 0x06, 0x39, 0xA8, 0x68, 0xCD, 0xA0, 0xC5, 0x69, 0x86, 0x1B
};

local mp2  = "AAFC0A3FE45E5E09DBE2C29";
local mp3  = "B55AA8DF8A7E83241F38AC7A9E479CAEF2E4D7C5";
local mp4  = "-63DBC2265B88268DC801C10EA68476B7BDE0090F";
local mp4a = "-DBC2265B88268DC801C10EA68476B7BDE0090F";
local mp5  = "F595CB42";
local mp5a = "-4B597E";
local mp6  = "0";
local mp7  = "EBFA7121CD838CE6439CC59DDB4CBEF3";
local mp8  = "5";
local mp9  = "F74A2876A1432698923B0767DA19DCF3D71795EE";
local mp10 = "9184E72A000";
local mp11 = "54D79A3557E8";
local mp12 = "10000000000000000";
local mp13 = "" ..
"34584F700C15A341E40BF7BFDD88A6630C8FF2B2067469372D391342BDAB6163963C" ..
"D5A5C79F708BDE26E0CCF2DB66CD6D6089E29A877C45F2B050D226E6DA88";
local mp14 = "" ..
"AC3FA0EABAAC45724814D798942A1E28E14C81E0DE8055CED630E7689DA648683645DB6E" ..
"458D9F5338CC3D4E33A5D1C9BF42780133599E60DEE0049AFA8F9489501AE5C9AA2B8C13" ..
"FD21285A538B2CA87A626BB56E0A654C8707535E637FF4E39174157402BDE3AA30C9F134" ..
"0C1307BAA864B075A9CC828B6A5E2B2BF1AE406D920CC5E7657D7C0E697DEE5375773AF9" ..
"E200A1B8FAD7CD141F9EE47ABB55511FEB9A4D99EBA22F3A3FF6792FA7EE9E5DC0EE94F7" ..
"7A631EDF3D7DD7C2DAAAFDF234D60302AB63D5234CEAE941B9AF0ADDD9E6E3A940A94EE5" ..
"5DB45A7C66E61EDD0477419BBEFA44C325129601C4F45671C6A0E64665DF341D17FBC71F" ..
"77418BD9F4375DDB3B9D56126526D8E5E0F35A121FD4F347013DA880020A752324F31DDD" ..
"9BCDB13A3B86E207A2DE086825E6EEB87B3A64232CFD8205B799BC018634AAE193F19531" ..
"D6EBC19A75F27CFFAA03EB5974898F53FD569AA5CE60F431B53B0CDE715A5F382405C9C4" ..
"761A8E24888328F09F7BCE4E8D80C957DF177629C8421ACCD0C268C63C0DD47C3C0D954F" ..
"D79F7D7297C6788DF4B3E51381759864D880ACA246DF09533739B8BB6085EAF7AE8DC2D9" ..
"F224E6874926C8D24D34B457FD2C9A586C6B99582DC24F787A39E3942786CF1D494B6EB4" ..
"A513498CDA0B217C4E80BCE7DA1C704C35E071AC21E0DA9F57C27C3533F46A8D20B04137" ..
"C1B1384BE4B2EB46";
local mp15 = "" ..
"39849CF7FD65AF2E3C4D87FE5526221103D90BA26A6642FFE3C3ECC0887BBBC57E011BF1" ..
"05D822A841653509C68F79EBE51C0099B8CBB04DEF31F36F5954208A3209AC122F0E11D8" ..
"4AE67A494D78336A2066D394D42E27EF6B03DDAF6D69F5112C93E714D27C94F82FC7EF77" ..
"445768C68EAE1C4A1407BE1B303243391D325090449764AE469CC53EC8012C4C02A72F37" ..
"07ED7275D2CC8D0A14B5BCC6BF264941520EBA97E3E6BAE4EE8BC87EE0DDA1F5611A6ECB" ..
"65F8AEF4F184E10CADBDFA5A2FEF828901D18C20785E5CC63473D638762DA80625003711" ..
"9E984AC43E707915B133543AF9D5522C3E7180DC58E1E5381C1FB7DC6A5F4198F3E88FA6" ..
"CBB6DFA8B2D1C763226B253E18BCCB79A29EE82D2DE735078C8AE3C3C86D476AAA08434C" ..
"09C274BDD40A1D8FDE38D6536C22F44E807EB73DE4FB36C9F51E0BC835DDBE3A8EFCF2FE" ..
"672B525769DC39230EE624D5EEDBD837C82A52E153F37378C3AD68A81A7ADBDF3345DBCE" ..
"8FA18CA1DE618EF94DF72EAD928D4F45B9E51632ACF158CF8332C51891D1D12C2A7E6684" ..
"360C4BF177C952579A9F442CFFEC8DAE4821A8E7A31C4861D8464CA9116C60866C5E72F7" ..
"434ADBED36D54ACDFDFF70A4EFB46E285131FE725F1C637D1C62115EDAD01C4189716327" ..
"BFAA79618B1656CBFA22C2C965687D0381CC2FE0245913C4D8D96108213680BD8E93E821" ..
"822AD9DDBFE4BD04";
local mp16 = "4A724340668DB150339A70";
local mp17 = "8ADB90F58";
local mp18 = "C64C230AB20E5";
local mp19 = "C9E0D699EF49732ADEC008500DEFC849";
local mp20 = "9C327F47DFD2B2D628A0D8A774FE6D0B";
local mp21 = "" ..
"DE8B393028A4A089D12B798B4D36FD4345AD5C74AECCF1B99C642F6FD3E55D50";
local mp22 = "" ..
"221C07E06FBB2F88919E4784E2C8DF7D902F1DF5B73CF1663010052A27887619";
local mp23 = "-33F337B742C7FD";
local mp24 = "76505FA6A59C";
local mp25 = "5192296858534827628530496329240383";
local mp25x = "10000000000000000000000004F3F";
local mp26 = "" ..
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001";

local md1 = 0;
local md2 = 0x1;
local md3 = 0x80;
local md4 = 0x9C97;
local md5 = 0xF5BF;
local md6 = 0x14A0;
local md7 = 0x03E8;
local md8 = 0x0101;
local md9 = 0xA;

-- Solutions of the form x_mpABC, where:
-- 
-- x = (p)roduct, (s)um, (d)ifference, (q)uotient, (r)emainder, (g)cd,
--     (i)nverse, (e)xponent, square roo(t), (g)cd, (l)cm,
--     (bit) (set) (clr) count.
--
--     A leading 'm' indicates a modular operation, e.g. ms_mp12 is the
--     modular sum of operands 1 and 2
-- 
-- ABC are the operand numbers involved in the computation.  If a 'd'
-- precedes the number, it is a digit operand; if a 'c' precedes it,
-- it is a constant; otherwise, it is a full integer.  

local p_mp12   = "4286AD72E095C9FE009938750743174ADDD7FD1E53";
local p_mp34   = "" ..
"-46BDBD66CA108C94A8CF46C325F7B6E2F2BA82D35" ..
"A1BFD6934C441EE369B60CA29BADC26845E918B";
local p_mp57   = "E260C265A0A27C17AD5F4E59D6E0360217A2EBA6";
local p_mp22   = "7233B5C1097FFC77CCF55928FDC3A5D31B712FDE7A1E91";
local p_mp1d4  = "3CECEA2331F4220BEF68DED";
local p_mp8d6  = "6720";
local p_mp5a11 = "-18F8D2E45B32C8EC30";
local p_mp1113 = "" ..
"11590FC3831C8C3C51813142C88E566408DB04F9E27642F6471A1822E0100B12F7F1" ..
"5699A127C0FA9D26DCBFF458522661F30C6ADA4A07C8C90F9116893F6DBFBF24C3A2" ..
"4340";
local p_mp1415 = "" ..
"26B36540DE8B3586699CCEAE218A2842C7D5A01590E70C4A26E789107FBCDB06AA2C" ..
"6DDC39E6FA18B16FCB2E934C9A5F844DAD60EE3B1EA82199EC5E9608F67F860FB965" ..
"736055DF0E8F2540EB28D07F47E309B5F5D7C94FF190AB9C83A6970160CA700B1081" ..
"F60518132AF28C6CEE6B7C473E461ABAC52C39CED50A08DD4E7EA8BA18DAD545126D" ..
"A388F6983C29B6BE3F9DCBC15766E8E6D626A92C5296A9C4653CAE5788350C0E2107" ..
"F57E5E8B6994C4847D727FF1A63A66A6CEF42B9C9E6BD04C92550B85D5527DE8A132" ..
"E6BE89341A9285C7CE7FB929D871BBCBD0ED2863B6B078B0DBB30FCA66D6C64284D6" ..
"57F394A0271E15B6EC7A9D530EBAC6CA262EF6F97E1A29FCE7749240E4AECA591ECF" ..
"272122BC587370F9371B67BB696B3CDC1BC8C5B64B6280994EBA00CDEB8EB0F5D06E" ..
"18F401D65FDCECF23DD7B9BB5B4C5458AEF2CCC09BA7F70EACB844750ACFD027521E" ..
"2E047DE8388B35F8512D3DA46FF1A12D4260213602BF7BFFDB6059439B1BD0676449" ..
"8D98C74F48FB3F548948D5BA0C8ECFCD054465132DC43466D6BBD59FBAF8D6D4E157" ..
"2D612B40A956C7D3E140F3B8562EF18568B24D335707D5BAC7495014DF2444172426" ..
"FD099DED560D30D1F945386604AFC85C64BD1E5F531F5C7840475FC0CF0F79810012" ..
"4572BAF5A9910CDBD02B27FFCC3C7E5E88EF59F3AE152476E33EDA696A4F751E0AE4" ..
"A3D2792DEA78E25B9110E12A19EFD09EA47FF9D6594DA445478BEB6901EAF8A35B2D" ..
"FD59BEE9BF7AA8535B7D326EFA5AA2121B5EBE04DD85827A3D43BD04F4AA6D7B62A2" ..
"B6D7A3077286A511A431E1EF75FCEBA3FAE9D5843A8ED17AA02BBB1B571F904699C5" ..
"A6073F87DDD012E2322AB3F41F2A61F428636FE86914148E19B8EF8314ED83332F2F" ..
"8C2ADE95071E792C0A68B903E060DD322A75FD0C2B992059FCCBB58AFA06B50D1634" ..
"BBD93F187FCE0566609FCC2BABB269C66CEB097598AA17957BB4FDA3E64A1B30402E" ..
"851CF9208E33D52E459A92C63FBB66435BB018E155E2C7F055E0B7AB82CD58FC4889" ..
"372ED9EEAC2A07E8E654AB445B9298D2830D6D4DFD117B9C8ABE3968927DC24B3633" ..
"BAD6E6466DB45DDAE87A0AB00336AC2CCCE176704F7214FCAB55743AB76C2B6CA231" ..
"7984610B27B5786DE55C184DDF556EDFEA79A3652831940DAD941E243F482DC17E50" ..
"284BC2FB1AD712A92542C573E55678878F02DFD9E3A863C7DF863227AEDE14B47AD3" ..
"957190124820ADC19F5353878EDB6BF7D0C77352A6E3BDB53EEB88F5AEF6226D6E68" ..
"756776A8FB49B77564147A641664C2A54F7E5B680CCC6A4D22D894E464DF20537094" ..
"548F1732452F9E7F810C0B4B430C073C0FBCE03F0D03F82630654BCE166AA772E1EE" ..
"DD0C08D3E3EBDF0AF54203B43AFDFC40D8FC79C97A4B0A4E1BEB14D8FCEFDDED8758" ..
"6ED65B18";

local mp_mp345 = "B9B6D3A3";
local mp_mp335 = "16609C2D";

local s_mp13   = "B55AA8DF8A7E83241F38B2B446B06A4FB84E5DE0";
local s_mp34   = "517EE6B92EF65C965736EB6BF7C325F73504CEB6";
local s_mp46   = "-63DBC2265B88268DC801C10EA68476B7BDE0090F";
local s_mp5d4  = "F59667D9";
local s_mp2d5  = "AAFC0A3FE45E5E09DBF21E8";
local s_mp1415 = "" ..
"E5C43DE2B811F4A084625F96E9504039E5258D8348E698CEB9F4D4292622042DB446" ..
"F75F4B65C1FB7A317257FA354BB5A45E789AEC254EAECE11F80A53E3B513822491DB" ..
"D9399DEC4807A2A3A10360129AC93F4A42388D3BF20B310DD0E9E9F4BE07FC88D53A" ..
"78A26091E0AB506A70813712CCBFBDD440A69A906E650EE090FDD6A42A95AC1A414D" ..
"317F1A9F781E6A30E9EE142ECDA45A1E3454A1417A7B9A613DA90831CF88EA1F2E82" ..
"41AE88CC4053220903C2E05BCDD42F02B8CF8868F84C64C5858BAD356143C5494607" ..
"EE22E11650148BAF65A985F6FC4CA540A55697F2B5AA95D6B8CF96EF638416DE1DD6" ..
"3BA9E2C09E22D03E75B60BE456C642F86B82A709253E5E087B507DE3A45F8392423F" ..
"4DBC284E8DC88C43CA77BC8DCEFB6129A59025F80F90FF978116DEBB9209E306FBB9" ..
"1B6111F8B8CFACB7C7C9BC12691C22EE88303E1713F1DFCEB622B8EA102F6365678B" ..
"C580ED87225467AA78E875868BD53B17574BA59305BC1AC666E4B7E9ED72FCFC200E" ..
"189D98FC8C5C7533739C53F52DDECDDFA5A8668BFBD40DABC9640F8FCAE58F532940" ..
"8162261320A25589E9FB51B50F80056471F24B7E1AEC35D1356FC2747FFC13A04B34" ..
"24FCECE10880BD9D97CA8CDEB2F5969BF4F30256EB5ED2BCD1DC64BDC2EE65217848" ..
"48A37FB13F84ED4FB7ACA18C4639EE64309BDD3D552AEB4AAF44295943DC1229A497" ..
"A84A";

local ms_mp345 = "1E71E292";

local d_mp12   = "-AAFBA6A55DD183FD854A60E";
local d_mp34   = "119366B05E606A9B1E73A6D8944CC1366B0C4E0D4";
local d_mp5d4  = "F5952EAB";
local d_mp6d2  = "-1";
local d_mp2324 = "-34698816E96D99";
local md_mp345 = "26596B86";

local q_mp11   = "1";
local r_mp11   = "0";
local q_mp23   = "0";
local r_mp23   = "AAFC0A3FE45E5E09DBE2C29";
local q_mp42   = "-95825A1FFA1A155D5";
local r_mp42   = "-6312E99D7700A3DCB32ADF2";
local q_mp45a  = "15344CDA3D841F661D2B61B6EDF7828CE36";
local r_mp45a  = "-47C47B";
local q_mp7c2  = "75FD3890E6C1C67321CE62CEEDA65F79";
local q_mp3d6  = "8CAFD53C272BD6FE8B0847BDC3B539EFAB5C3";
local r_mp3d6  = "1E5";
local r_mp5d5  = "1257";
local r_mp47   = "B3A9018D970281A90FB729A181D95CB8";
local q_mp1404 = "" ..
"-1B994D869142D3EF6123A3CBBC3C0114FA071CFCEEF4B7D231D65591D32501AD80F" ..
"FF49AE4EC80514CC071EF6B42521C2508F4CB2FEAD69A2D2EF3934087DCAF88CC4C4" ..
"659F1CA8A7F4D36817D802F778F1392337FE36302D6865BF0D4645625DF8BB044E19" ..
"930635BE2609FAC8D99357D3A9F81F2578DE15A300964188292107DAC980E0A08CD7" ..
"E938A2135FAD45D50CB1D8C2D4C4E60C27AB98B9FBD7E4DBF752C57D2674520E4BB2" ..
"7E42324C0EFE84FB3E38CF6950E699E86FD45FE40D428400F2F94EDF7E94FAE10B45" ..
"89329E1BF61E5A378C7B31C9C6A234F8254D4C24823B84D0BF8D671D8BC9154DFAC9" ..
"49BD8ACABD6BD32DD4DC587F22C86153CB3954BDF7C2A890D623642492C482CF3E2C" ..
"776FC019C3BBC61688B485E6FD35D6376089C1E33F880E84C4E51E8ABEACE1B3FB70" ..
"3EAD0E28D2D44E7F1C0A859C840775E94F8C1369D985A3C5E8114B21D68B3CBB75D2" ..
"791C586153C85B90CAA483E57A40E2D97950AAB84920A4396C950C87C7FFFE748358" ..
"42A0BF65445B26D40F05BE164B822CA96321F41D85A289C5F5CD5F438A78704C9683" ..
"422299D21899A22F853B0C93081CC9925E350132A0717A611DD932A68A0ACC6E4C7F" ..
"7F685EF8C1F4910AEA5DC00BB5A36FCA07FFEAA490C547F6E14A08FE87041AB803E1" ..
"BD9E23E4D367A2C35762F209073DFF48F3";
local r_mp1404 = "12FF98621ABF63144BFFC3207AC8FC10D8D1A09";

local q_mp13c  = "" ..
"34584F700C15A341E40BF7BFDD88A6630C8FF2B2067469372D391342" ..
"BDAB6163963CD5A5C79F708BDE26E0CCF2DB66CD6D6089E29A877C45";
local r_mp13c  = "F2B050D226E6DA88";
local q_mp9c16 = "F74A2876A1432698923B0767DA19DCF3D71795E";
local r_mp9c16 = "E";

local r_mp1920 = "2DAE57520F76C054B61F2FA898F15B3E";
local r_mp2122 = "" ..
"11E309ED8A4183566775CC6DFC81C051E492A8B2635F49547C041072E6B298BA";

local e_mp5d9 = "A8FD7145E727A20E52E73D22990D35D158090307A" ..
                         "13A5215AAC4E9AB1E96BD34E531209E03310400";
local e_mp78  = "AA5F72C737DFFD8CCD108008BFE7C79ADC01A819B" ..
                         "32B75FB82EC0FB8CA83311DA36D4063F1E57857A2" ..
                         "1AB226563D84A15BB63CE975FF1453BD6750C58D9" ..
                         "D113175764F5D0B3C89B262D4702F4D9640A3";
local me_mp817 = "E504493ACB02F7F802B327AB13BF25";
local me_mp5d47 = "1D45ED0D78F2778157992C951DD2734C";
local me_mp1512 = "FB5B2A28D902B9D9";
local me_mp161718 = "423C6AC6DBD74";
local me_mp5114 = "" ..
"64F0F72807993578BBA3C7C36FFB184028F9EB9A810C92079E1498D8A80FC848E1F0" ..
"25F1DE43B7F6AC063F5CC29D8A7C2D7A66269D72BF5CDC327AF88AF8EF9E601DCB0A" ..
"3F35BFF3525FB1B61CE3A25182F17C0A0633B4089EA15BDC47664A43FEF639748AAC" ..
"19CF58E83D8FA32CD10661D2D4210CC84792937E6F36CB601851356622E63ADD4BD5" ..
"542412C2E0C4958E51FD2524AABDC7D60CFB5DB332EEC9DC84210F10FAE0BA2018F2" ..
"14C9D6867C9D6E49CF28C18D06CE009FD4D04BFC8837C3FAAA773F5CCF6DED1C22DE" ..
"181786AFE188540586F2D74BF312E595244E6936AE52E45742109BAA76C36F2692F5" ..
"CEF97AD462B138BE92721194B163254CBAAEE9B9864B21CCDD5375BCAD0D24132724" ..
"113D3374B4BCF9AA49BA5ACBC12288C0BCF46DCE6CB4A241A91BD559B130B6E9CD3D" ..
"D7A2C8B280C2A278BA9BF5D93244D563015C9484B86D9FEB602501DC16EEBC3EFF19" ..
"53D7999682BF1A1E3B2E7B21F4BDCA3C355039FEF55B9C0885F98DC355CA7A6D8ECF" ..
"5F7F1A6E11A764F2343C823B879B44616B56BF6AE3FA2ACF5483660E618882018E3F" ..
"C8459313BACFE1F93CECC37B2576A5C0B2714BD3EEDEEC22F0E7E3E77B11396B9B99" ..
"D683F2447A4004BBD4A57F6A616CDDFEC595C4FC19884CC2FC21CF5BF5B0B81E0F83" ..
"B9DDA0CF4DFF35BB8D31245912BF4497FD0BD95F0C604E26EA5A8EA4F5EAE870A5BD" ..
"FE8C";

local e_mpc2d3 = "100000000000000000000000000000000";

local t_mp9    = "FB9B6E32FF0452A34746";
local i_mp27   = "B6AD8DCCDAF92B6FE57D062FFEE3A99";
local t_mp15 = "" ..
"795853094E59B0008093BCA8DECF68587C64BDCA2F3F7F8963DABC12F1CFFFA9B8C4" ..
"365232FD4751870A0EF6CA619287C5D8B7F1747D95076AB19645EF309773E9EACEA0" ..
"975FA4AE16251A8DA5865349C3A903E3B8A2C0DEA3C0720B6020C7FED69AFF62BB72" ..
"10FAC443F9FFA2950776F949E819260C2AF8D94E8A1431A40F8C23C1973DE5D49AA2" ..
"0B3FF5DA5C1D5324E712A78FF33A9B1748F83FA529905924A31DF38643B3F693EF9B" ..
"58D846BB1AEAE4523ECC843FF551C1B300A130B65C1677402778F98C51C10813250E" ..
"2496882877B069E877B59740DC1226F18A5C0F66F64A5F59A9FAFC5E9FC45AEC0E7A" ..
"BEE244F7DD3AC268CF512A0E52E4F5BE5B94";

local g_mp71   = "1";
local g_mp1426 = "1";
local g_mp1011 = "3E8";

local g_mp25   = "7";
local l_mp1011 = "C589E3D7D64A6942A000";

local set_mp26 = 129;
local clr_mp26 = 95;
local bit_mp26 = 224;

local set_mp15 = 2045;
local clr_mp15 = 2049;
local bit_mp15 = 4094;

local set_md3 = 1;
local clr_md3 = 7;
local bit_md3 = 8;

local set_md7 = 6;
local clr_md7 = 4;
local bit_md7 = 10;

-- mp9 in radices from 5 to 64 inclusive
local LOW_RADIX = 5;
local HIGH_RADIX = 64;
local v_mp9 = {
  "404041130042310320100141302000203430214122130002340212132414134210033",
  "44515230120451152500101352430105520150025145320010504454125502",
  "644641136612541136016610100564613624243140151310023515322",
  "173512120732412062323044435407317550316717172705712756",
  "265785018434285762514442046172754680368422060744852",
  "1411774500397290569709059837552310354075408897518",
  "184064268501499311A17746095910428222A241708032A",
  "47706011B225950B02BB45602AA039893118A85950892",
  "1A188C826B982353CB58422563AC602B783101671A86",
  "105957B358B89B018958908A9114BC3DDC410B77982",
  "CB7B3387E23452178846C55DD9D70C7CA9AEA78E8",
  "F74A2876A1432698923B0767DA19DCF3D71795EE",
  "17BF7C3673B76D7G7A5GA836277296F806E7453A",
  "2EBG8HH3HFA6185D6H0596AH96G24C966DD3HG2",
  "6G3HGBFEG8I3F25EAF61B904EIA40CFDH2124F",
  "10AHC3D29EBHDF3HD97905CG0JA8061855C3FI",
  "3BA5A55J5K699B2D09C38A4B237CH51IHA132",
  "EDEA90DJ0B5CB3FGG1C8587FEB99D3C143CA",
  "31M26JI1BBD56K3I028MML4EEDMAJK60LGLE",
  "GGG5M3142FKKG82EJ28111D70EMHC241E4E",
  "4446F4D5H10982023N297BF0DKBBHLLJB0I",
  "12E9DEEOBMKAKEP0IM284MIP7FO1O521M46",
  "85NN0HD48NN2FDDB1F5BMMKIB8CK20MDPK",
  "2D882A7A0O0JPCJ4APDRIB77IABAKDGJP2",
  "MFMCI0R7S27AAA3O3L2S8K44HKA7O02CN",
  "7IGQS73FFSHC50NNH44B6PTTNLC3M6H78",
  "2KLUB3U9850CSN6ANIDNIF1LB29MJ43LH",
  "UT52GTL18CJ9H4HR0TJTK6ESUFBHF5FE",
  "BTVL87QQBMUGF8PFWU4W3VU7U922QTMW",
  "4OG10HW0MSWJBIDEE2PDH24GA7RIHIAA",
  "1W8W9AX2DRUX48GXOLMK0PE42H0FEUWN",
  "SVWI84VBH069WR15W1U2VTK06USY8Z2",
  "CPTPNPDa5TYCPPNLALENT9IMX2GL0W2",
  "5QU21UJMRaUYYYYYN6GHSMPOYOXEEUY",
  "2O2Q7C6RPPB1SXJ9bR4035SPaQQ3H2W",
  "18d994IbT4PHbD7cGIPCRP00bbQO0bc",
  "NcDUEEWRO7XT76260WGeBHPVa72RdA",
  "BbX2WCF9VfSB5LPdJAdeXKV1fd6LC2",
  "60QDKW67P4JSQaTdQg7JE9ISafLaVU",
  "33ba9XbDbRdNF4BeDB2XYMhAVDaBdA",
  "1RIPZJA8gT5L5H7fTcaRhQ39geMMTc",
  "d65j70fBATjcDiidPYXUGcaBVVLME",
  "LKA9jhPabDG612TXWkhfT2gMXNIP2",
  "BgNaYhjfT0G8PBcYRP8khJCR3C9QE",
  "6Wk8RhJTAgDh10fYAiUVB1aM0HacG",
  "3dOCjaf78kd5EQNViUZWj3AfFL90I",
  "290VWkL3aiJoW4MBbHk0Z0bDo22Ni",
  "1DbDZ1hpPZNUDBUp6UigcJllEdC26",
  "dFSOLBUM7UZX8Vnc6qokGIOiFo1h",
  "NcoUYJOg0HVmKI9fR2ag0S8R2hrK",
  "EOpiJ5Te7oDe2pn8ZhAUKkhFHlZh",
  "8nXK8rp8neV8LWta1WDgd1QnlWsU",
  "5T3d6bcSBtHgrH9bCbu84tblaa7r",
  "3PlUDIYUvMqOVCir7AtquK5dWanq",
  "2A70gDPX2AtiicvIGGk9poiMtgvu",
  "1MjiRxjk10J6SVAxFguv9kZiUnIc",
  "rpre2vIDeb4h3sp50r1YBbtEx9L",
  "ZHcoip0AglDAfibrsUcJ9M1C8fm",
  "NHP18+eoe6uU54W49Kc6ZK7+bT2",
  "FTAA7QXGoQOaZi7PzePtFFN5vNk"
};

local b_mp4a = {
  0x01, 0xDB, 0xC2, 0x26, 0x5B, 0x88, 0x26, 0x8D, 
  0xC8, 0x01, 0xC1, 0x0E, 0xA6, 0x84, 0x76, 0xB7, 
  0xBD, 0xE0, 0x09, 0x0F
};

-- ########################################################################

local tests = {};
local tests_map = {};

function register_test(name, func)
    tests[#tests + 1] = {name, func};
    tests_map[name] = {name, func, #tests};
end

-- ########################################################################

-- test_copy()
function test_copy()
    local a = {0, sign = '+'};
    local b = {0, sign = '+'};
    local passes = 0;
    local tests = 0;
    local passed;

    mpi_set(a, mp3, 16);
    mpi_copy(b, a);

    tests = tests + 1;
    if (a.sign ~= b.sign or #a ~= #b) then
        if (a.sign ~= b.sign) then
            print(("error: sign of original is %s, sign of copy is %s."):format(a.sign, b.sign))
        else
            print(("error: original precision is %s, copy precision is %s."):format(#a, #b));
        end
    else
        passes = passes + 1;
    end

    tests = tests + 1;
    passed = true;
    for ix=1,#b do
        if a[ix] ~= b[ix] then
            print(("error: digit %d " .. DIGIT_FMT .. " ~= " .. DIGIT_FMT .. "."):format(ix, a[ix], b[ix]));
            passed = false;
        end
    end
    if passed then
       passes = passes + 1; 
    end

    mpi_set(a, mp5a, 16);
    mpi_copy(b, a);

    tests = tests + 1;
    if (a.sign ~= b.sign or #a ~= #b) then
        if (a.sign ~= b.sign) then
            print(("error: sign of original is %s, sign of copy is %s."):format(a.sign, b.sign))
        else
            print(("error: original precision is %s, copy precision is %s."):format(#a, #b));
        end
    else
        passes = passes + 1;
    end

    tests = tests + 1;
    passed = true;
    for ix=1,#b do
        if a[ix] ~= b[ix] then
            print(("error: digit %d " .. DIGIT_FMT .. " ~= " .. DIGIT_FMT .. "."):format(ix, a[ix], b[ix]));
            passed = false;
        end
    end
    if passed then
       passes = passes + 1; 
    end

    return passes, tests;
end
register_test('copy', test_copy);

--[[
-- ########################################################################

/* {{{ test_exch() */

int test_exch(void)
{
  mp_int  a, b;

  mp_init(&a); mp_init(&b);
  mp_read_radix(&a, mp7, 16); mp_read_radix(&b, mp1, 16);

  mp_exch(&a, &b);
  mp_toradix(&a, g_intbuf, 16);

  mp_clear(&a);
  if(u_strcmp(g_intbuf, mp1) != 0) {
    mp_clear(&b);
    reason("error: exchange failed\n");
    return 1;
  }

  mp_toradix(&b, g_intbuf, 16);

  mp_clear(&b);
  if(u_strcmp(g_intbuf, mp7) != 0) {
    reason("error: exchange failed\n");
    return 1;
  }

  return 0;
}

/* }}} */

-- ########################################################################

--]]


--
-- test_zero()
function test_zero()
    local a = {0, sign = '+'};
    local tests = 0;
    local passes = 0;

    mpi_set(a, mp7, 16);
    mpi_zero(a);

    tests = tests + 1;
    if #a ~= 1 or a[1] ~= 0 then
        print("error: result is " .. mpi_tostring(a, 16));
    else
        passes = passes + 1;
    end

    return passes, tests;
end
register_test('zero', test_zero)

-- ########################################################################
-- test_set()
function test_set()
    local tests = 0;
    local passes = 0;
    local a = {0, sign = '+'};

    -- Test single digit set
    tests = tests + 1;
    mpi_set_d(a, 5);
    if (a[1] ~= 5) then
        print("error: result is " .. mpi_tostring(a) .. ", expected 5");
    else
        passes = passes + 1;
    end

    -- Test integer set
    tests = tests + 1;
    mpi_set(a, -4938110);
    local temp = mpi_tostring(a, 16);
    if temp ~= mp5a then
        print("error: result is " .. temp .. ", expected " .. mp5a);
    else
        passes = passes + 1;
    end

    return passes, tests;
end
register_test('set', test_set)

-- ########################################################################

-- test_abs()
function test_abs()
    local tests = 0;
    local passes = 0;
    local a = {0, sign = '+'};

    tests = tests + 1;
    mpi_set(a, mp4, 16);
    mpi_abs(a, a);

    if (a.sign ~= '+') then
        print("error: sign is " .. a.sign .. ", expected '+'");
    else
        passes = passes + 1;
    end

    return passes, tests;
end
register_test('abs', test_abs)

-- ########################################################################

-- test_neg()
function test_neg()
    local tests = 0;
    local passes = 0;
    local a = {0, sign = '+'};

    mpi_set(a, mp4, 16);

    tests = tests + 1;
    local s = a.sign;
    mpi_neg(a, a);
    if (a.sign == s) then
        print("error: sign of result is same as sign of nonzero input\n");
    else
        passes = passes + 1;
    end

    return passes, tests;
end
register_test('neg', test_neg)

-- ########################################################################

-- test_add_d()
function test_add_d()
    local tests = 0;
    local passes = 0;
    local a = {0, sign = '+'};
    local temp;

    tests = tests + 1;
    mpi_set(a, mp5, 16);
    mpi_add_d(a, md4, a);

    temp = mpi_tostring(a, 16);
    if (temp ~= s_mp5d4) then
        print("error: computed " .. temp .. ", expected " .. s_mp5d4);
    else
        passes = passes + 1;
    end

    tests = tests + 1;
    mpi_set(a, mp2, 16);
    mpi_add_d(a, md5, a);

    temp = mpi_tostring(a, 16);
    if (temp ~= s_mp2d5) then
        print("error: computed " .. temp .. ", expected " .. s_mp2d5);
    else
        passes = passes + 1;
    end

    return passes, tests;
end
register_test('add_d', test_add_d)

-- ########################################################################

-- test_add()
function test_add()
    local tests = 0;
    local passes = 0;
    local a = {0, sign = '+'};
    local b = {0, sign = '+'};
    local temp;

    tests = tests + 1;
    mpi_set(a, mp1, 16);
    mpi_set(b, mp3, 16);
    mpi_add(a, b, a);

    temp = mpi_tostring(a, 16);
    if (temp ~= s_mp13) then
        print("error: computed " .. temp .. ", expected " .. s_mp13);
    else
        passes = passes + 1;
    end

    tests = tests + 1;
    mpi_set(a, mp4, 16);
    mpi_add(a, b, a);

    temp = mpi_tostring(a, 16);
    if (temp ~= s_mp34) then
        print("error: computed " .. temp .. ", expected " .. s_mp34);
    else
        passes = passes + 1;
    end

    tests = tests + 1;
    mpi_set(a, mp4, 16);
    mpi_set(b, mp6, 16);
    mpi_add(a, b, a);

    temp = mpi_tostring(a, 16);
    if (temp ~= s_mp46) then
        print("error: computed " .. temp .. ", expected " .. s_mp46);
    else
        passes = passes + 1;
    end

    tests = tests + 1;
    mpi_set(a, mp14, 16);
    mpi_set(b, mp15, 16);
    mpi_add(a, b, a);

    temp = mpi_tostring(a, 16);
    if (temp ~= s_mp1415) then
        print("error: computed " .. temp .. ", expected " .. s_mp1415);
    else
        passes = passes + 1;
    end

    return passes, tests;
end
register_test('add', test_add)

-- ########################################################################

-- test_sub_d()
function test_sub_d()
    local tests = 0;
    local passes = 0;
    local a = {0, sign = '+'};
    local temp;

    tests = tests + 1;
    mpi_set(a, mp5, 16);
    mpi_sub_d(a, md4, a);

    temp = mpi_tostring(a, 16);
    if (temp ~= d_mp5d4) then
        print("error: computed " .. temp .. ", expected " .. d_mp5d4);
    else
        passes = passes + 1;
    end

    tests = tests + 1;
    mpi_set(a, mp6, 16);
    mpi_sub_d(a, md2, a);

    temp = mpi_tostring(a, 16);
    if (temp ~= d_mp6d2) then
        print("error: computed " .. temp .. ", expected " .. d_mp6d2);
    else
        passes = passes + 1;
    end

    return passes, tests;
end
register_test('sub_d', test_sub_d)

-- ########################################################################

-- test_sub()
function test_sub()
    local tests = 0;
    local passes = 0;
    local a = {0, sign = '+'};
    local b = {0, sign = '+'};
    local c = {0, sign = '+'};
    local temp;

    tests = tests + 1;
    mpi_set(a, mp1, 16);
    mpi_set(b, mp2, 16);
    mpi_sub(a, b, a);

    temp = mpi_tostring(a, 16);
    if (temp ~= d_mp12) then
        print("error: computed " .. temp .. ", expected " .. d_mp12);
    else
        passes = passes + 1;
    end

    tests = tests + 1;
    mpi_set(a, mp3, 16);
    mpi_set(b, mp4, 16);
    mpi_sub(a, b, a);

    temp = mpi_tostring(a, 16);
    if (temp ~= d_mp34) then
        print("error: computed " .. temp .. ", expected " .. d_mp34);
    else
        passes = passes + 1;
    end

    tests = tests + 1;
    mpi_set(a, mp23, 16);
    mpi_set(b, mp24, 16);
    mpi_sub(a, b, c);

    temp = mpi_tostring(c, 16);
    if (temp ~= d_mp2324) then
        print("error: computed " .. temp .. ", expected " .. d_mp2324);
    else
        passes = passes + 1;
    end

    return passes, tests;
end
register_test('sub', test_sub);


-- ########################################################################

-- test_mul_d()
function test_mul_d()
    local tests = 0;
    local passes = 0;
    local a = {0, sign = '+'};
    local temp;

    tests = tests + 1;
    mpi_set(a, mp1, 16);
    mpi_mul_d(a, md4, a);

    temp = mpi_tostring(a, 16);
    if (temp ~= p_mp1d4) then
        print("error: computed " .. temp .. ", expected " .. p_mp1d4);
    else
        passes = passes + 1;
    end

    tests = tests + 1;
    mpi_set(a, mp8, 16);
    mpi_mul_d(a, md6, a);

    temp = mpi_tostring(a, 16);
    if (temp ~= p_mp8d6) then
        print("error: computed " .. temp .. ", expected " .. p_mp8d6);
    else
        passes = passes + 1;
    end

    return passes, tests;
end
register_test('mul_d', test_mul_d);

-- ########################################################################

-- test_mul()
function test_mul()
    local tests = 0;
    local passes = 0;
    local a = {0, sign = '+'};
    local b = {0, sign = '+'};
    local c = {0, sign = '+'};
    local temp;

    tests = tests + 1;
    mpi_set(a, mp1, 16);
    mpi_set(b, mp2, 16);
    mpi_mul(a, b, c);

    temp = mpi_tostring(c, 16);
    if (temp ~= p_mp12) then
        print("error: computed " .. temp .. ", expected " .. p_mp12);
    else
        passes = passes + 1;
    end

    tests = tests + 1;
    mpi_set(a, mp3, 16);
    mpi_set(b, mp4, 16);
    mpi_mul(a, b, c);

    temp = mpi_tostring(c, 16);
    if (temp ~= p_mp34) then
        print("error: computed " .. temp .. ", expected " .. p_mp34);
    else
        passes = passes + 1;
    end

    tests = tests + 1;
    mpi_set(a, mp5, 16);
    mpi_set(b, mp7, 16);
    mpi_mul(a, b, c);

    temp = mpi_tostring(c, 16);
    if (temp ~= p_mp57) then
        print("error: computed " .. temp .. ", expected " .. p_mp57);
    else
        passes = passes + 1;
    end

    tests = tests + 1;
    mpi_set(a, mp11, 16);
    mpi_set(b, mp13, 16);
    mpi_mul(a, b, c);

    temp = mpi_tostring(c, 16);
    if (temp ~= p_mp1113) then
        print("error: computed " .. temp .. ", expected " .. p_mp1113);
    else
        passes = passes + 1;
    end

    tests = tests + 1;
    mpi_set(a, mp14, 16);
    mpi_set(b, mp15, 16);
    mpi_mul(a, b, c);

    temp = mpi_tostring(c, 16);
    if (temp ~= p_mp1415) then
        print("error: computed " .. temp .. ", expected " .. p_mp1415);
    else
        passes = passes + 1;
    end

    tests = tests + 1;
    mpi_set(a, mp5a, 16);
    mpi_set(b, mp11, 16);
    mpi_mul(a, b, c);

    temp = mpi_tostring(c, 16);
    if (temp ~= p_mp5a11) then
        print("error: computed " .. temp .. ", expected " .. p_mp5a11);
    else
        passes = passes + 1;
    end

    return passes, tests;
end
register_test('mul', test_mul)

-- ########################################################################

-- test_sqr()
function test_sqr()
    local tests = 0;
    local passes = 0;
    local a = {0, sign = '+'};
    local temp;

    tests = tests + 1;
    mpi_set(a, mp2, 16);
    mpi_sqr(a, a);

    temp = mpi_tostring(a, 16);
    if (temp ~= p_mp22) then
        print("error: computed " .. temp .. ", expected " .. p_mp22);
    else
        passes = passes + 1;
    end

    return passes, tests;
end
register_test('sqr', test_sqr)

-- ########################################################################

-- test_div_d()
function test_div_d()
    local tests = 0;
    local passes = 0;
    local a = {0, sign = '+'};
    local q = {0, sign = '+'};
    local r = 0;
    local temp;

    mpi_set(a, mp3, 16);
    r = mpi_div_d(a, md6, q);

    tests = tests + 1;
    temp = mpi_tostring(q, 16);
    if (temp ~= q_mp3d6) then
        print("error: computed q = " .. temp .. ", expected " .. q_mp3d6);
    else
        passes = passes + 1;
    end

    tests = tests + 1;
    temp = ("%X"):format(r);
    if (temp ~= r_mp3d6) then
        print("error: computed r = " .. temp .. ", expected " .. r_mp3d6);
    else
        passes = passes + 1;
    end

    mpi_set(a, mp9, 16);
    r = mpi_div_d(a, 16, q);

    tests = tests + 1;
    temp = mpi_tostring(q, 16);
    if (temp ~= q_mp9c16) then
        print("error: computed q = " .. temp .. ", expected " .. q_mp9c16);
    else
        passes = passes + 1;
    end

    tests = tests + 1;
    temp = ("%X"):format(r);
    if (temp ~= r_mp9c16) then
        print("error: computed r = " .. temp .. ", expected " .. r_mp9c16);
    else
        passes = passes + 1;
    end

    return passes, tests;
end
register_test('div_d', test_div_d)


-- ########################################################################

-- test_div_2()
function test_div_2()
    local tests = 0;
    local passes = 0;
    local a = {0, sign = '+'};

    tests = tests + 1;
    mpi_set(a, mp7, 16);
    mpi_div_2(a, a);

    temp = mpi_tostring(a, 16);
    if (temp ~= q_mp7c2) then
        print("error: computed " .. temp .. ", expected " .. q_mp7c2);
    else
        passes = passes + 1;
    end

    return passes, tests;
end
register_test('div_2', test_div_2)

-- ########################################################################

-- test_div_2d()

function test_div_2d()
    local tests = 0;
    local passes = 0;
    local a = {0, sign = '+'};
    local q = {0, sign = '+'};
    local r = {0, sign = '+'};
    local temp;

    mpi_set(a, mp13, 16);

    mpi_div_2d(a, 64, q, r);

    tests = tests + 1;
    temp = mpi_tostring(q, 16);
    if (temp ~= q_mp13c) then
        print("error: test #1, computed q = " .. temp .. ", computed r = " .. mpi_tostring(r, 16) .. ", expected q = " .. q_mp13c .. ", r = " .. r_mp13c);
    else
        passes = passes + 1;
    end

    tests = tests + 1;
    temp = mpi_tostring(r, 16);
    if (temp ~= r_mp13c) then
        print("error: test #1,computed r = " .. temp .. ", expected " .. r_mp13c);
    else
        passes = passes + 1;
    end

    return passes, tests;
end
register_test('div_2d', test_div_2d)

-- ########################################################################

-- test_div()
function test_div()
    local tests = 0;
    local passes = 0;
    local a = {0, sign='+'};
    local b = {0, sign='+'};
    local q = {0, sign='+'};
    local r = {0, sign='+'};
    local temp;
    local err = 0;

    mpi_set(a, mp2, 16);
    mpi_set(b, mp3, 16);
    mpi_div(a, b, q, r);

    tests = tests + 1;
    temp = mpi_tostring(q, 16);
    if (temp ~= q_mp23) then
        print("error: computed q = " .. temp .. ", r = " .. mpi_tostring(r, 16) .. ", expected q = " .. q_mp23 .. ", r = " .. r_mp23);
    else
        passes = passes + 1;
    end

    tests = tests + 1;
    temp = mpi_tostring(r, 16);
    if (temp ~= r_mp23) then
        print("error: computed q = " .. mpi_tostring(q, 16) .. ", r = " .. temp .. ", expected q = " .. q_mp23 .. ", r = " .. r_mp23);
    else
        passes = passes + 1;
    end

    mpi_set(a, mp4, 16);
    mpi_set(b, mp2, 16);
    mpi_div(a, b, q, r);

    tests = tests + 1;
    temp = mpi_tostring(q, 16);
    if (temp ~= q_mp42) then
        print("error: computed quot " .. temp .. ", expected " .. q_mp42);
    else
        passes = passes + 1;
    end

    tests = tests + 1;
    temp = mpi_tostring(r, 16);
    if (temp ~= r_mp42) then
        print("error: computed rem " .. temp .. ", expected " .. r_mp42);
    else
        passes = passes + 1;
    end

    mpi_set(a, mp1, 16);
    mpi_set(b, mp1, 16);
    mpi_div(a, b, q, r);

    tests = tests + 1;
    temp = mpi_tostring(q, 16);
    if (temp ~= q_mp11) then
        print("error: computed quot " .. temp .. ", expected " .. q_mp11);
    else
        passes = passes + 1;
    end

    tests = tests + 1;
    temp = mpi_tostring(r, 16);
    if (temp ~= r_mp11) then
        print("error: computed rem " .. temp .. ", expected " .. r_mp11);
    else
        passes = passes + 1;
    end

    mpi_set(a, mp4, 16);
    mpi_set(b, mp5a, 16);
    mpi_div(a, b, q, r);

    tests = tests + 1;
    temp = mpi_tostring(q, 16);
    if (temp ~= q_mp45a) then
        print("error: computed quot " .. temp .. ", expected " .. q_mp45a);
    else
        passes = passes + 1;
    end

    tests = tests + 1;
    temp = mpi_tostring(r, 16);
    if (temp ~= r_mp45a) then
        print("error: computed rem " .. temp .. ", expected " .. r_mp45a);
    else
        passes = passes + 1;
    end

    mpi_set(a, mp14, 16);
    mpi_set(b, mp4, 16);
    mpi_div(a, b, q, r);

    tests = tests + 1;
    temp = mpi_tostring(q, 16);
    if (temp ~= q_mp1404) then
        print("error: computed quot " .. temp .. ", expected " .. q_mp1404);
    else
        passes = passes + 1;
    end

    tests = tests + 1;
    temp = mpi_tostring(r, 16);
    if (temp ~= r_mp1404) then
        print("error: computed rem " .. temp .. ", expected " .. r_mp1404);
    else
        passes = passes + 1;
    end

    return passes, tests;
end
register_test('div', test_div)

-- ########################################################################

-- test_expt_d()
function test_expt_d()
    local tests = 0;
    local passes = 0;
    local a = {0, sign='+'};
    local temp;

    mpi_set(a, mp5, 16);

    mpi_expt_d(a, md9, a);

    tests = tests + 1;
    temp = mpi_tostring(a, 16);
    if (temp ~= e_mp5d9) then
        print("error: computed " .. temp .. ", expected " .. e_mp5d9);
    else
        passes = passes + 1;
    end

    return passes, tests;
end
register_test('expt_d', test_expt_d)

-- ########################################################################

-- test_expt()
function test_expt()
    local tests = 0;
    local passes = 0;
    local a = {0, sign='+'};
    local b = {0, sign='+'};
    local temp;

    mpi_set(a, mp7, 16);
    mpi_set(b, mp8, 16);

    mpi_expt(a, b, a);

    tests = tests + 1;
    temp = mpi_tostring(a, 16);
    if (temp ~= e_mp78) then
        print("error: computed " .. temp .. ", expected " .. e_mp78);
    else
        passes = passes + 1;
    end

    return passes, tests;
end
register_test('expt', test_expt)

-- ########################################################################

-- test_2expt()
function test_2expt()
    local tests = 0;
    local passes = 0;
    local a = {0, sign='+'};
    local temp;

    mpi_2expt(a, md3);

    tests = tests + 1;
    temp = mpi_tostring(a, 16);
    if (temp ~= e_mpc2d3) then
        print("error: computed " .. temp .. ", expected " .. e_mpc2d3);
    else
        passes = passes + 1;
    end

    return passes, tests;
end
register_test('2expt', test_2expt)

-- ########################################################################

-- test_sqrt() 
function test_sqrt()
    local tests = 0;
    local passes = 0;
    local a = {0, sign = '+'};
    local temp;

    mpi_set(a, mp9, 16);
    mpi_sqrt(a, a);

    tests = tests + 1;
    temp = mpi_tostring(a, 16);
    if (temp ~= t_mp9) then
        print("error: computed " .. temp .. ", expected " .. t_mp9);
    else
        passes = passes + 1;
    end

    -- mpi_set(a, mp15, 16);
    -- mpi_sqrt(a, a);
    -- 
    -- tests = tests + 1;
    -- temp = mpi_tostring(a, 16);
    -- if (temp ~= t_mp15) then
    --     print("error: computed " .. temp .. ", expected " .. t_mp15);
    -- else
    --     passes = passes + 1;
    -- end

    return passes, tests;
end
register_test('sqrt', test_sqrt);


-- ########################################################################

-- test_mod_d()
function test_mod_d()
    local tests = 0;
    local passes = 0;
    local a = {0, sign = '+'};
    local r;
    local temp;

    mpi_set(a, mp5, 16);
    r = mpi_mod_d(a, md5);

    tests = tests + 1;
    temp = ("%X"):format(r);
    if temp ~= r_mp5d5 then
        print("error: computed " .. temp .. ", expected " .. r_mp5d5);
    else
        passes = passes + 1;
    end

    return passes, tests;
end
register_test('mod_d', test_mod_d);

-- ########################################################################

-- test_mod()
function test_mod()
    local tests = 0;
    local passes = 0;
    local a = {0, sign = '+'};
    local m = {0, sign = '+'};
    local c = {0, sign = '+'};
    local temp;

    mpi_set(a, mp4, 16);
    mpi_set(m, mp7, 16);
    mpi_mod(a, m, c);

    tests = tests + 1;
    temp = mpi_tostring(c, 16);
    if (temp ~= r_mp47) then
        print("error: computed " .. temp .. ", expected " .. r_mp47);
    else
        passes = passes + 1;
    end

    mpi_set(a, mp19, 16);
    mpi_set(m, mp20, 16);
    mpi_mod(a, m, c);

    tests = tests + 1;
    temp = mpi_tostring(c, 16);
    if (temp ~= r_mp1920) then
        print("error: computed " .. temp .. ", expected " .. r_mp1920);
    else
        passes = passes + 1;
    end

    mpi_set(a, mp21, 16);
    mpi_set(m, mp22, 16);
    mpi_mod(a, m, c);

    tests = tests + 1;
    temp = mpi_tostring(c, 16);
    if (temp ~= r_mp2122) then
        print("error: computed " .. temp .. ", expected " .. r_mp2122);
    else
        passes = passes + 1;
    end

    return passes, tests;
end
register_test('mod', test_mod);

-- ########################################################################

-- test_addmod()
function test_addmod()
    local tests = 0;
    local passes = 0;
    local a = {0, sign = '+'};
    local b = {0, sign = '+'};
    local m = {0, sign = '+'};
    local c = {0, sign = '+'};
    local temp;

    mpi_set(a, mp3, 16);
    mpi_set(b, mp4, 16);
    mpi_set(m, mp5, 16);
    mpi_addmod(a, b, m, c);

    tests = tests + 1;
    temp = mpi_tostring(c, 16);
    if (temp ~= ms_mp345) then
        print("error: computed " .. temp .. ", expected " .. ms_mp345);
    else
        passes = passes + 1;
    end

    return passes, tests;
end
register_test('addmod', test_addmod);

-- ########################################################################

-- test_submod()
function test_submod()
    local tests = 0;
    local passes = 0;
    local a = {0, sign = '+'};
    local b = {0, sign = '+'};
    local m = {0, sign = '+'};
    local c = {0, sign = '+'};
    local temp;

    mpi_set(a, mp3, 16);
    mpi_set(b, mp4, 16);
    mpi_set(m, mp5, 16);
    mpi_submod(a, b, m, c);

    tests = tests + 1;
    temp = mpi_tostring(c, 16);
    if (temp ~= md_mp345) then
        print("error: computed " .. temp .. ", expected " .. md_mp345);
    else
        passes = passes + 1;
    end

    return passes, tests;
end
register_test('submod', test_submod);

-- ########################################################################

-- test_mulmod()
function test_mulmod()
    local tests = 0;
    local passes = 0;
    local a = {0, sign = '+'};
    local b = {0, sign = '+'};
    local m = {0, sign = '+'};
    local c = {0, sign = '+'};
    local temp;

    mpi_set(a, mp3, 16);
    mpi_set(b, mp4, 16);
    mpi_set(m, mp5, 16);
    mpi_mulmod(a, b, m, c);

    tests = tests + 1;
    temp = mpi_tostring(c, 16);
    if (temp ~= mp_mp345) then
        print("error: computed " .. temp .. ", expected " .. mp_mp345);
    else
        passes = passes + 1;
    end

    return passes, tests;
end
register_test('mulmod', test_mulmod);

-- ########################################################################

-- test_sqrmod()
function test_sqrmod()
    local tests = 0;
    local passes = 0;
    local a = {0, sign = '+'};
    local m = {0, sign = '+'};
    local c = {0, sign = '+'};
    local temp;

    mpi_set(a, mp3, 16);
    mpi_set(m, mp5, 16);
    mpi_sqrmod(a, m, c);

    tests = tests + 1;
    temp = mpi_tostring(c, 16);
    if (temp ~= mp_mp335) then
        print("error: computed " .. temp .. ", expected " .. mp_mp335);
    else
        passes = passes + 1;
    end

    return passes, tests;
end
register_test('sqrmod', test_sqrmod);

-- ########################################################################

-- test_exptmod()
function test_exptmod()
    local tests = 0;
    local passes = 0;
    local a = {0, sign = '+'};
    local b = {0, sign = '+'};
    local m = {0, sign = '+'};
    local c = {0, sign = '+'};
    local temp;
    local err = 0;

    mpi_set(a, mp8, 16);
    mpi_set(b, mp1, 16);
    mpi_set(m, mp7, 16);

    -- Test 1
    tests = tests + 1;
    print("    " .. tests .. ")");
    mpi_exptmod(a, b, m, c);

    temp = mpi_tostring(c, 16);
    if (temp ~= me_mp817) then
        print("error: computed " .. temp .. ", expected " .. me_mp817);
    else
        passes = passes + 1;
    end

    mpi_set(a, mp1, 16);
    mpi_set(b, mp5, 16);
    mpi_set(m, mp12, 16);

    -- Test 2
    tests = tests + 1;
    print("    " .. tests .. ")");
    mpi_exptmod(a, b, m, c);

    temp = mpi_tostring(c, 16);
    if (temp ~= me_mp1512) then
        print("error: computed " .. temp .. ", expected " .. me_mp1512);
    else
        passes = passes + 1;
    end

    mpi_set(a, mp5, 16);
    mpi_set(b, mp1, 16);
    mpi_set(m, mp14, 16);

    -- Test 3
    tests = tests + 1;
    print("    " .. tests .. ")");
    mpi_exptmod(a, b, m, c);
    
    temp = mpi_tostring(c, 16);
    if (temp ~= me_mp5114) then
        print("error: computed " .. temp .. ", expected " .. me_mp5114);
    else
        passes = passes + 1;
    end

    mpi_set(a, mp16, 16);
    mpi_set(b, mp17, 16);
    mpi_set(m, mp18, 16);

    -- Test 4
    tests = tests + 1;
    print("    " .. tests .. ")");
    mpi_exptmod(a, b, m, c);

    temp = mpi_tostring(c, 16);
    if (temp ~= me_mp161718) then
        print("error: computed " .. temp .. ", expected " .. me_mp161718);
    else
        passes = passes + 1;
    end

    mpi_set_d(a, 2);
    mpi_set(b, mp26, 16);
    mpi_set(m, mp26, 16);

    -- Test 5
    tests = tests + 1;
    print("    " .. tests .. ")");
    mpi_exptmod(a, b, m, c);

    temp = mpi_tostring(c, 16);
    if (temp ~= "2") then
        print("error: computed " .. temp .. ", expected " .. "2");
    else
        passes = passes + 1;
    end

    return passes, tests;
end
register_test('exptmod', test_exptmod);

-- ########################################################################

-- test_exptmod_d()
function test_exptmod_d()
    local tests = 0;
    local passes = 0;
    local a = {0, sign='+'};
    local m = {0, sign='+'};
    local temp;

    mpi_set(a, mp5, 16);
    mpi_set(m, mp7, 16);

    tests = tests + 1;
    mpi_exptmod_d(a, md4, m, a);

    temp = mpi_tostring(a, 16);
    if temp ~= me_mp5d47 then
        print("error: computed " .. temp .. ", expected " .. me_mp5d47);
    else
        passes = passes + 1;
    end

    return passes, tests;
end
register_test('exptmod_d', test_exptmod_d);

-- ########################################################################

-- test_invmod()
function test_invmod()
    local tests = 0;
    local passes = 0;
    local a = {0, sign='+'};
    local m = {0, sign='+'};
    local temp;

    mpi_set(a, mp2, 16);
    mpi_set(m, mp7, 16);

    tests = tests + 1;
    if not mpi_invmod(a, m, a) then
        print("error: no inverse mod found.");
    else
        passes = passes + 1;
    end

    tests = tests + 1;
    temp = mpi_tostring(a, 16);
    if temp ~= i_mp27 then
        print("error: computed " .. temp .. ", expected " .. i_mp27);
    else
        passes = passes + 1;
    end

    return passes, tests;
end
register_test('invmod', test_invmod);

-- ########################################################################

-- test_cmp_d()
function test_cmp_d()
    local tests = 0;
    local passes = 0;
    local a = {0, sign = '+'};
    local temp;
    local result;

    tests = tests + 1;
    mpi_set(a, mp8, 16);
    if (mpi_cmp_d(a, md8) >= 0) then
        print("error: " .. mp8 .. " >= " .. DIGIT_FMT:format(md8));
    else
        passes = passes + 1;
    end

    tests = tests + 1;
    mpi_set(a, mp5, 16);
    if (mpi_cmp_d(a, md8) <= 0) then
        print("error: " .. mp5 .. " <= " .. DIGIT_FMT:format(md8));
    else
        passes = passes + 1;
    end

    tests = tests + 1;
    mpi_set(a, mp6, 16);
    if (mpi_cmp_d(a, md1) ~= 0) then
        print("error: " .. mp6 .. " != " .. DIGIT_FMT:format(md1));
    else
        passes = passes + 1;
    end

    return passes, tests;
end
register_test('cmp_d', test_cmp_d);

-- ########################################################################

-- test_cmp_z()
function test_cmp_z()
    local tests = 0;
    local passes = 0;
    local a = {0, sign = '+'};
    local temp;
    local result;

    tests = tests + 1;
    mpi_set(a, mp6, 16);
    if (mpi_cmp_z(a) ~= 0) then
        print("error: someone thinks a zero value is non-zero");
    else
        passes = passes + 1;
    end

    tests = tests + 1;
    mpi_set(a, mp1, 16);
    if (mpi_cmp_z(a) <= 0) then
        print("error: someone thinks a positive value is non-positive");
    else
        passes = passes + 1;
    end

    tests = tests + 1;
    mpi_set(a, mp4, 16);
    if (mpi_cmp_z(a) >= 0) then
        print("error: someone thinks a negative value is non-negative");
    else
        passes = passes + 1;
    end

    return passes, tests;
end
register_test('cmp_z', test_cmp_z);

-- ########################################################################

-- test_cmp()
function test_cmp(void)
    local tests = 0;
    local passes = 0;
    local a = {0, sign='+'};
    local b = {0, sign='+'};

    tests = tests + 1;

    mpi_set(a, mp3, 16);
    mpi_set(b, mp4, 16);
    if (mpi_cmp(a, b) <= 0) then
        print("error: " .. mp3 .. " <= " .. mp4);
    else
        passes = passes + 1;
    end

    tests = tests + 1;
    mpi_set(b, mp3, 16);
    if (mpi_cmp(a, b) ~= 0) then
        print("error: " .. mp3 .. " ~= " .. mp3);
    else
        passes = passes + 1;
    end

    tests = tests + 1;
    mpi_set(a, mp5, 16);
    if (mpi_cmp(a, b) >= 0) then
        print("error: " .. mp5 .. " >= " .. mp3);
    else
        passes = passes + 1;
    end

    return passes, tests;
end
register_test('cmp', test_cmp);

--[[

-- ########################################################################

-- test_cmp_mag()
int test_cmp_mag(void)
{
  mp_int  a, b;

  mp_init(&a); mp_init(&b);
  mp_read_radix(&a, mp5, 16); mp_read_radix(&b, mp4, 16);

  if(mp_cmp_mag(&a, &b) >= 0) {
    reason("error: %s >= %s\n", mp5, mp4);
    mp_clear(&a); mp_clear(&b);
    return 1;
  }

  mp_read_radix(&b, mp5, 16);
  if(mp_cmp_mag(&a, &b) != 0) {
    reason("error: %s != %s\n", mp5, mp5);
    mp_clear(&a); mp_clear(&b);
    return 1;
  }

  mp_read_radix(&a, mp1, 16);
  if(mp_cmp_mag(&b, &a) >= 0) {
    reason("error: %s >= %s\n", mp5, mp1);
    mp_clear(&a); mp_clear(&b);
    return 1;
  }

  mp_clear(&a); mp_clear(&b);
  return 0;

}

--]]

-- ########################################################################

-- test_parity()
function test_parity()
    local tests = 0;
    local passes = 0;
    local a = {0, sign='+'};
    local b = {0, sign='+'};

    tests = tests + 1;
    mpi_set(a, mp1, 16);

    if (not mpi_isodd(a)) then
        print("error: expected operand to be odd, but it isn't");
    else
        passes = passes + 1;
    end

    tests = tests + 1;
    mpi_set(a, mp6, 16);
    if (not mpi_iseven(a)) then
      print("error: expected operand to be even, but it isn't");
    else
        passes = passes + 1;
    end

    return passes, tests;
end
register_test('parity', test_parity);

-- ########################################################################

-- test_gcd()
function test_gcd()
    local tests = 0;
    local passes = 0;
    local a = {0, sign='+'};
    local b = {0, sign='+'};
    local c = {0, sign='+'};
    local temp;

    tests = tests + 1;
    mpi_set(a, mp7, 16);
    mpi_set(b, mp1, 16);

    mpi_gcd(a, b, c);

    temp = mpi_tostring(c, 16);
    if temp ~= g_mp71 then
        print("error: computed " .. temp .. ", expected " .. g_mp71);
    else
        passes = passes + 1;
    end

    tests = tests + 1;
    mpi_set(a, mp14, 16);
    mpi_set(b, mp26, 16);

    mpi_gcd(a, b, c);

    temp = mpi_tostring(c, 16);
    if temp ~= g_mp1426 then
        print("error: computed " .. temp .. ", expected " .. g_mp1426);
    else
        passes = passes + 1;
    end

    tests = tests + 1;
    mpi_set(a, mp10, 16);
    mpi_set(b, mp11, 16);

    mpi_gcd(a, b, c);

    temp = mpi_tostring(c, 16);
    if temp ~= g_mp1011 then
        print("error: computed " .. temp .. ", expected " .. g_mp1011);
    else
        passes = passes + 1;
    end

    return passes, tests;
end
register_test('gcd', test_gcd);

-- ########################################################################

-- test_lcm()
function test_lcm()
    local tests = 0;
    local passes = 0;
    local a = {0, sign='+'};
    local b = {0, sign='+'};
    local c = {0, sign='+'};
    local temp;

    tests = tests + 1;
    mpi_set(a, mp10, 16);
    mpi_set(b, mp11, 16);

    mpi_lcm(a, b, c);

    temp = mpi_tostring(c, 16);
    if temp ~= l_mp1011 then
        print("error: computed " .. temp .. ", expected " .. l_mp1011);
    else
        passes = passes + 1;
    end

    return passes, tests;
end
register_test('lcm', test_lcm);

-- ########################################################################

-- test_convert()
function test_convert()
    local tests = 0;
    local passes = 0;
    local a = {0, sign = '+'};
    local temp;
    local result;

    mpi_set(a, mp9, 16);
    for ix=LOW_RADIX,HIGH_RADIX do
        temp = mpi_tostring(a, ix);
        result = v_mp9[ix - LOW_RADIX + 1];

        tests = tests + 1;
        if (temp ~= result) then
            print("error: radix " .. ix .. ", computed " .. temp ..", expected " .. result);
        else
            passes = passes + 1;
        end
    end

    tests = tests + 1;
    mpi_set(a, mp25, 10);
    temp = mpi_tostring(a, 16);
    if temp ~= mp25x then
        print("error: computed " .. temp .. ", expected " .. mp25x);
    else
        passes = passes + 1;
    end

    return passes, tests;
end
register_test('convert', test_convert);


-- ########################################################################

-- test_counts()
function test_counts()
    local tests = 0;
    local passes = 0;
    local a = {0, sign = '+'};
    local temp;
    local err=0;

    -- test mp26
    mpi_set(a, mp26, 16);

    tests = tests + 1;
    temp = mpi_setcount(a);
    if temp ~= set_mp26 then
        print("error: set_mp26: computed " .. temp .. ", expected " .. set_mp26);
    else
        passes = passes + 1;
    end

    tests = tests + 1;
    temp = mpi_clrcount(a);
    if temp ~= clr_mp26 then
        print("error: clr_mp26: computed " .. temp .. ", expected " .. clr_mp26);
    else
        passes = passes + 1;
    end

    tests = tests + 1;
    temp = mpi_bitcount(a);
    if temp ~= bit_mp26 then
        print("error: bit_mp26: computed " .. temp .. ", expected " .. bit_mp26);
    else
        passes = passes + 1;
    end

    -- test mp15
    mpi_set(a, mp15, 16);

    tests = tests + 1;
    temp = mpi_setcount(a);
    if temp ~= set_mp15 then
        print("error: set_mp15: computed " .. temp .. ", expected " .. set_mp15);
    else
        passes = passes + 1;
    end

    tests = tests + 1;
    temp = mpi_clrcount(a);
    if temp ~= clr_mp15 then
        print("error: clr_mp15: computed " .. temp .. ", expected " .. clr_mp15);
    else
        passes = passes + 1;
    end

    tests = tests + 1;
    temp = mpi_bitcount(a);
    if temp ~= bit_mp15 then
        print("error: bit_mp15: computed " .. temp .. ", expected " .. bit_mp15);
    else
        passes = passes + 1;
    end

    -- test md3
    tests = tests + 1;
    temp = mpi_setcount_d(md3);
    if temp ~= set_md3 then
        print("error: set_md3: computed " .. temp .. ", expected " .. set_md3);
    else
        passes = passes + 1;
    end

    tests = tests + 1;
    temp = mpi_clrcount_d(md3);
    if temp ~= clr_md3 then
        print("error: clr_md3: computed " .. temp .. ", expected " .. clr_md3);
    else
        passes = passes + 1;
    end

    tests = tests + 1;
    temp = mpi_bitcount_d(md3);
    if temp ~= bit_md3 then
        print("error: bit_md3: computed " .. temp .. ", expected " .. bit_md3);
    else
        passes = passes + 1;
    end

    -- test md7
    tests = tests + 1;
    temp = mpi_setcount_d(md7);
    if temp ~= set_md7 then
        print("error: set_md7: computed " .. temp .. ", expected " .. set_md7);
    else
        passes = passes + 1;
    end

    tests = tests + 1;
    temp = mpi_clrcount_d(md7);
    if temp ~= clr_md7 then
        print("error: clr_md7: computed " .. temp .. ", expected " .. clr_md7);
    else
        passes = passes + 1;
    end

    tests = tests + 1;
    temp = mpi_bitcount_d(md7);
    if temp ~= bit_md7 then
        print("error: bit_md7: computed " .. temp .. ", expected " .. bit_md7);
    else
        passes = passes + 1;
    end

    return passes, tests;
end
register_test('counts', test_counts);

--[[
/* }}} */

-- ########################################################################

/* {{{ test_raw() */

int test_raw(void)
{
  int            len, out = 0;
  mp_int         a;
  unsigned char  *buf;

  mp_init(&a); mp_read_radix(&a, mp4a, 16);

  len = mp_signed_bin_size(&a);
  if(len != sizeof(b_mp4a)) {
    reason("error: expected length %d, computed %d\n", sizeof(b_mp4a),
	   len);
    mp_clear(&a);
    return 1;
  }

  buf = calloc(len, sizeof(char));
  mp_to_signed_bin(&a, buf);

  if(memcmp(buf, b_mp4a, len) != 0) {
    reason("error: binary output does not match test vector\n");
    out = 1;
  }

  mp_zero(&a);
  mp_to_unsigned_bin(&a, buf);
  if(buf[0] != 0) {
    reason("error: zero is handled incorrectly in mp_to_unsigned_bin()\n");
    out = 1;
  }

  free(buf);
  mp_clear(&a);

  return out;

}

/* }}} */

-- ########################################################################
--]]

-- test_pprime()
function test_pprime()
    local tests = 0;
    local passes = 0;
    local p = {0, sign='+'};
    local err = 0;

    tests = tests + 1;
    mpi_set(p, mp7, 16);

    if mpi_pprime(p, 5) ~= true then
        print("error: " .. mp7 .. " failed Rabin-Miller test, but is prime");
    else
        passes = passes + 1;
    end

    return passes, tests;
end
register_test('pprime', test_pprime);

-- ########################################################################

-- test_fermat()
function test_fermat()
    local tests = 0;
    local passes = 0;
    local p = {0, sign='+'};
    local err = 0;

    tests = tests + 1;
    mpi_set(p, mp7, 16);
    if mpi_fermat(p, 2) ~= true then
        print("error: " .. mp7 .. " failed Fermat test on 2, but is prime");
    else
        passes = passes + 1;
    end

    tests = tests + 1;
    if mpi_fermat(p, 3) ~= true then
        print("error: " .. mp7 .. " failed Fermat test on 3, but is prime");
        err = 1;
    else
        passes = passes + 1;
    end

    return passes, tests;
end
register_test('fermat', test_fermat);

-- ########################################################################

function do_test_all()
    print("----------------------------------------------------------------------");
    local total_tests = 0;
    local total_passed = 0;
    local total_start = os.time();
    for ix=1,#tests do
        local test = tests[ix];

        print("-- performing test '" .. test[1] .. "'");
        local test_start = os.time();
        local test_function = test[2];
        local test_passed, test_tests = test_function();
        local test_end = os.time();
        print("    Tests:  " .. test_tests);
        print("    Passed: " .. test_passed .. " (" .. ("%.2f"):format(test_passed / test_tests * 100) .. "%)");
        print("    Time:   " .. os.difftime(test_end, test_start) .. "secs")
        print("    [" .. (test_passed == test_tests and '  OK  ' or 'FAILED') .. "]");

        total_tests  = total_tests  + test_tests;
        total_passed = total_passed + test_passed;
    end
    local total_end = os.time();

    print("----------------------------------------------------------------------");
    print("Tests:  " .. total_tests);
    print("Passed: " .. total_passed .. " (" .. ("%.2f"):format(total_passed / total_tests * 100) .. "%)");
    print("Time:   " .. os.difftime(total_end, total_start) .. "secs")
    print("[" .. (total_passed == total_tests and '  OK  ' or 'FAILED') .. "]");
end


do_test_all();
